from symbide import PATHS, ModuleManager, ThreadExecutionManager, SubprocessExecutionManager
import gtk
import os.path
from time import strftime, localtime
from xml.sax.saxutils import unescape

categories = ['Execution', 'ExecutionLogs', 'Modules', 'Paths']

class Monitor(gtk.Window):
    '''
    Top window of the Monitor.
    '''
    
    def __init__(self, mainWindow):
        gtk.Window.__init__(self)
        self.set_default_size(600, 400)
        self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
        self.set_title('Symbide - Monitor')
        self.set_transient_for(mainWindow)
        self.show()
        self._createMainVBox()
        self.setCategory(categories[0])
        
    def _createMainVBox(self):
        self.MainVBox = gtk.VBox()
        self.add(self.MainVBox)
        self.MainVBox.show()
        self._createChooseBar()
        self._createView()
        self._createActionBar()
        
    def _createChooseBar(self):
        '''
        Creates the bar at top of the window, where one can choose
        between the possible views.
        '''
        
        self.chooseBar = gtk.HBox()
        self.MainVBox.pack_start(self.chooseBar, expand=False)
        self.chooseBar.show()
        
        for c in categories:
            button = gtk.Button(c)
            button.show()
            def changeCategory(button, category):
                self.setCategory(category)
            button.connect('clicked', changeCategory, c)
            button.set_relief(gtk.RELIEF_NONE)
            button.set_focus_on_click(False)    
            self.chooseBar.pack_start(button, expand=False, padding=1)
    
    def _createView(self):
        '''
        In this widget the actual choosen settings are displayed.
        '''

        self.view = gtk.ScrolledWindow()
        self.MainVBox.pack_start(self.view)
        self.view.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        self.view.show()
            
    def _createActionBar(self):
        '''
        Adds an exit and a revert button at the bottom of the window.
        '''
        
        self.actionBar = gtk.HBox()
        self.MainVBox.pack_end(self.actionBar, expand=False)
        self.actionBar.show()
        
        stocks = [gtk.STOCK_CLOSE, gtk.STOCK_REFRESH]
        connections = [self._event_close, self._event_refresh]
        buttons = [gtk.Button(stock=s) for s in stocks]
        
        for s in stocks:
            button = gtk.Button(stock=s)
            self.actionBar.pack_end(button, expand=False)
            button.show()
            button.set_focus_on_click(False)
            button.connect('clicked', connections.pop(0))
    
    def _event_close(self, *args):
        self.destroy()
        
    def _event_refresh(self, *args):
        self.setCategory(self.lastCategory)
        
    def setCategory(self, category):
        self.lastCategory = category
        child = self.view.get_child() 
        if child:
            child.destroy()
        child = eval('View' + category)()
        self.view.add_with_viewport(child)


class ViewExecution(gtk.HBox):            
    def __init__(self):
        gtk.HBox.__init__(self)
        self.show()
        
        self.lastView = None
        
        self.leftVBox = gtk.VBox()
        self.leftVBox.show()
        self.pack_start(self.leftVBox, expand=False)
        
        self._createContentView()
        self._createThreadView()
        self._createSubprocessView()
    
    def _createContentView(self):
        self.contentView = gtk.TextView()
        self.contentView.show()
        self.pack_end(self.contentView)
        self.buffer = self.contentView.get_buffer()
        self.contentView.set_editable(False)
    
    def _createThreadView(self):
        threadLabel = gtk.Label('Threads:')
        threadLabel.show()
        threadLabel_alignment = gtk.Alignment(0,0,0,1)
        threadLabel_alignment.show()
        threadLabel_alignment.add(threadLabel)
        self.leftVBox.pack_start(threadLabel_alignment, expand=False)
        
        self.threadList = gtk.TreeView()
        self.threadList.show()
        self.threadList.connect('cursor-changed', self._event_cursor_changed)
        self.leftVBox.pack_start(self.threadList, expand=False)
        
        self.renderer = gtk.CellRendererText()
        self.renderer.set_property('cell-background-gdk', gtk.gdk.color_parse('#EEEEEE'))
        self.threadList.set_headers_visible(False)
        self.threadModel = gtk.ListStore(str, str)
        self.threadList.set_model(self.threadModel)
        for i in range(2):
            self.threadList.append_column(gtk.TreeViewColumn(None, self.renderer, text=i))
        
        ThreadExecutionManager.inscribe(self.updateThreads)
        self.updateThreads()
    
    def _createSubprocessView(self):
        subprocLabel = gtk.Label('Subprocesses:')
        subprocLabel.show()
        subprocLabel_alignment = gtk.Alignment(0,0,0,1)
        subprocLabel_alignment.show()
        subprocLabel_alignment.add(subprocLabel)
        self.leftVBox.pack_start(subprocLabel_alignment, expand=False)
        
        self.subprocList = gtk.TreeView()
        self.subprocList.show()
        self.subprocList.connect('cursor-changed', self._event_cursor_changed)
        self.leftVBox.pack_start(self.subprocList, expand=False)
        
        self.subprocRenderer = gtk.CellRendererText()
        self.subprocRenderer.set_property('cell-background-gdk', gtk.gdk.color_parse('#EEEEEE'))
        self.subprocList.set_headers_visible(False)
        self.subprocModel = gtk.ListStore(str, str)
        self.subprocList.set_model(self.subprocModel)
        for i in range(2):
            self.subprocList.append_column(gtk.TreeViewColumn(None, self.renderer, text=i))
        
        SubprocessExecutionManager.inscribe(self.updateSubproc)
        self.updateSubproc()
    
    def updateThreads(self):
        self.threadModel.clear()
        self.active_thread_job = ThreadExecutionManager.active_job
        self.thread_jobs = ThreadExecutionManager.jobs
        
        if self.active_thread_job:
            self.threadModel.append([self.active_thread_job[2], self.active_thread_job[3]])
        
        for j in self.thread_jobs:
            self.threadModel.append((j[2], j[3]))
            
        if not self.thread_jobs and not self.active_thread_job:
            self.threadModel.append(('..', ''))
    
    def updateSubproc(self):
        self.subprocModel.clear()
        self.subproc_jobs = SubprocessExecutionManager.jobs
        
        for j in self.subproc_jobs:
            self.subprocModel.append((j[1], j[3]))
            
        if not self.subproc_jobs:
            self.subprocModel.append(('..',''))
    
    def _event_cursor_changed(self, treeview):
        pos = treeview.get_cursor()[0][0]
        if treeview is self.subprocList:
            self.buffer.set_text('')
            self.lastView = ''
            return
        if pos==0 and self.active_thread_job:
            if self.lastView==str(self.active_thread_job[2])+str(self.active_thread_job[3]):
                return
            self.lastView = str(self.active_thread_job[2])+str(self.active_thread_job[3])
            self.buffer.set_text(self.active_thread_job[1][0].totext())
        elif self.thread_jobs:
            pos = pos-1
            if self.lastView==str(self.thread_jobs[pos][2])+str(self.thread_jobs[pos][3]):
                return
            self.lastView = str(self.thread_jobs[pos][2])+str(self.thread_jobs[pos][3])
            self.buffer.set_text(self.thread_jobs[pos][1][0].totext())


class ViewExecutionLogs(gtk.HBox):
    def __init__(self):
        gtk.HBox.__init__(self)
        self.show()
        
        self._createContentViewer()
        self._createBrowser()
    
    def _createContentViewer(self):
        self.contentView = gtk.TextView()
        self.contentView.show()
        self.buffer = self.contentView.get_buffer()
        self.contentView.set_editable(False)
        self.pack_end(self.contentView)
        
    def _createBrowser(self):
        self.browser = gtk.TreeView()
        self.browser.show()
        self.pack_start(self.browser, expand=False)
        self.browser.connect('cursor-changed', self._event_cursor_changed)
        self.browser.set_headers_visible(False)
        
        self.renderer = gtk.CellRendererText()
        self.renderer.set_property('cell-background-gdk', gtk.gdk.color_parse('#EEEEEE'))
        
        self.model = gtk.ListStore(str, str)
        self.browser.set_model(self.model)
        
        for i in range(2):
            self.browser.append_column(gtk.TreeViewColumn(None, self.renderer, text=i))
        self.updateBrowser()
       
    def updateBrowser(self):
        dir = PATHS['log_executor']
        index = os.path.join(dir, 'index.txt')
        if not os.path.exists(index):
            self.model.append(["Can't find any execution log files", ''])
            return
        
        logfileIndex = open(index, 'r')
        self.logfiles = []
        for entry in logfileIndex:
            name, time = entry.split(' : ', 2)
            self.logfiles.append(name)
            t = strftime('%Y.%m.%d %H:%M:%S' ,localtime(float(time)))
            n = os.path.splitext(os.path.basename(name))[0]
            self.model.append([n, t])
        logfileIndex.close()
        
    def _event_cursor_changed(self, treeview):
        path = self.logfiles[treeview.get_cursor()[0][0]]
        self.viewFile(path)
        
    def viewFile(self, path):
        self.buffer.set_text('')
        if os.path.exists(path):
            f = open(path, 'r')
            buf = f.read()
            f.close()
            end1=0
            end = self.buffer.get_end_iter
            append = lambda text:self.buffer.insert(end(), text)
            marks = []
            while True:
                start1 = buf.find('<', end1)
                append(unescape(buf[end1+1:start1]))
                end1 = buf.find('>', start1)
                if start1==-1 or end1==-1:
                    break
                
                tag = buf[start1+1:end1].strip('/ ').split(' ')[0].strip()
                if buf[start1+1]=='/':
                    depth = 0
                    for m in marks:
                        if len(m)==2:
                            depth+=1
                            if m[0]==tag:
                                m.append(self.buffer.create_mark(None, end(), True))
                                append(' ')
                                m.append(depth-1)
                                break
                else:
                    marks.append([tag, self.buffer.create_mark(None, end(), True)])
                    append('  ')

                
            colors = ['#ffffff', '#ebebeb', '#d7d7d7', '#c3c3c3', '#afafaf', '#9b9b9b', '#878787', '#7f7f7f', '#707070', '#666666']
            iter = lambda mark:self.buffer.get_iter_at_mark(mark)
            for i in marks:
                tag = self.buffer.create_tag()
                tag.set_property('paragraph-background', colors[i[3]])
                tag.set_property('left-margin', i[3]*15)
                tag.set_property('scale', 0.96)
                self.buffer.apply_tag(tag, iter(i[1]), iter(i[2]))
                
                endpos = iter(i[1])
                self.buffer.insert(endpos, '\n'+i[0]+'\n')
                tag = self.buffer.create_tag()
                tag.set_property('weight', 700)
                self.buffer.apply_tag(tag, iter(i[1]), endpos)
                
            tag = self.buffer.create_tag()
            tag.set_property('scale', 1.5)
            self.buffer.apply_tag(tag, self.buffer.get_start_iter(), end())


class ViewModules(gtk.VBox):
    ''' 
    Lists all external dependencies and there availibility.
    '''
    
    def __init__(self):
        gtk.VBox.__init__(self)
        self.show()
        
        stocks = {True: gtk.STOCK_YES,
                  False: gtk.STOCK_NO,
                  None: gtk.STOCK_DIALOG_QUESTION}
        
        modules = ModuleManager.getModules()
        line = 0
        for module, available in modules:
            button = gtk.Button(stock=stocks[available])
            button.get_children()[0].get_children()[0].get_children()[1].set_text('')
            button.set_relief(gtk.RELIEF_NONE)
            button.set_focus_on_click(False)
            button.show()
            
            def importModule(button, module):
                if ModuleManager.hasModule(module):
                    button.set_label(stocks[True])
                else:
                    button.set_label(stocks[False])
                button.get_children()[0].get_children()[0].get_children()[1].set_text('')
                button.show()
                    
            button.connect('clicked', importModule, module)
            
            label = gtk.Label(module)
            label.show()
            
            hbox = gtk.HBox()
            hbox.show()
            hbox.pack_start(button, expand=False)
            hbox.pack_start(label, expand=False)
            
            self.pack_start(hbox, expand=False)


class ViewPaths(gtk.Table):
    ''' 
    Lists all paths in PATHS in a pretty manner.
    '''
    
    def __init__(self):
        gtk.Table.__init__(self)
        self.show()
        
        paths = PATHS.items()
        paths.sort(key=lambda x:x[0])
        
        line=0
        for name, ps in paths:
            id = gtk.Label(name)
            id.show()
            id_alignment = gtk.Alignment(0,0,0,1)
            id_alignment.show()
            id_alignment.add(id)
            
            pathBox = gtk.VBox()
            pathBox.show()
            pathBox_alignment = gtk.Alignment(0,0,0,1)
            pathBox_alignment.show()
            pathBox_alignment.add(pathBox)
            for p in ps:
                pathLabel = gtk.Label(p)
                pathLabel.show()
                pathLabel_alignment = gtk.Alignment(0,0,0,1)
                pathLabel_alignment.show()
                pathLabel_alignment.add(pathLabel)
                pathBox.pack_start(pathLabel_alignment)
            self.show_all()   
            self.attach(id_alignment, 0, 1, line, line+1, xpadding=5)
            self.attach(pathBox_alignment, 1, 2, line, line+1, ypadding=5, xpadding=5)
            line+=1
           
if __name__=="__main__":
    w = None
    m = Monitor(w)
    m.connect('delete-event', gtk.main_quit)
    m.connect('destroy-event', gtk.main_quit)
    gtk.main()